# -*- coding: utf-8 -*-
"""
Chapter 4: Form Handling and Validation

This demo covers:
- Basic form handling without external libraries
- WTForms integration with Flask-WTF
- Form validation techniques
- File upload handling
- CSRF protection
- Flash messaging for user feedback
"""

import os
import secrets

from flask import Flask, render_template, request, redirect, url_for, flash, session
from werkzeug.utils import secure_filename

from forms import RegistrationForm

app = Flask(__name__)
app.secret_key = os.getenv('SECRET_KEY', secrets.token_hex(16))

# Configure upload settings
app.config['MAX_CONTENT_LENGTH'] = 16 * 1024 * 1024  # 16MB max file size
app.config['UPLOAD_FOLDER'] = os.path.join(os.getcwd(), 'uploads')

# Ensure upload directory exists
os.makedirs(app.config['UPLOAD_FOLDER'], exist_ok=True)


# Helper functions for manual form validation
def validate_name(name):
    """Validate name field."""
    if not name or not name.strip():
        return "Name is required"
    if len(name.strip()) < 2:
        return "Name must be at least 2 characters long"
    return None


def validate_email(email):
    """Validate email field."""
    if not email or not email.strip():
        return "Email is required"
    if "@" not in email or "." not in email:
        return "Please enter a valid email address"
    return None


def validate_age(age):
    """Validate age field."""
    if not age:
        return None  # Age is optional
    try:
        age_int = int(age)
        if age_int < 1 or age_int > 120:
            return "Age must be between 1 and 120"
    except ValueError:
        return "Age must be a number"
    return None


def validate_account_type(account_type):
    """Validate account type field."""
    if not account_type:
        return "Please select an account type"
    if account_type not in ['personal', 'business']:
        return "Invalid account type"
    return None


# Routes
@app.route('/')
def index():
    """Home page."""
    return render_template('index.html')


@app.route('/basic', methods=['GET', 'POST'])
def basic_form():
    """Demonstrate basic form handling without WTForms."""
    form_data = None
    errors = {}

    if request.method == 'POST':
        # Get form data
        name = request.form.get('name', '').strip()
        email = request.form.get('email', '').strip()
        age = request.form.get('age', '').strip()
        bio = request.form.get('bio', '').strip()
        preferences = request.form.getlist('preferences')
        account_type = request.form.get('account_type', '').strip()

        # Validate form data
        errors['name'] = validate_name(name)
        errors['email'] = validate_email(email)
        errors['age'] = validate_age(age)
        errors['account_type'] = validate_account_type(account_type)

        # Check if there are any errors
        if not any(errors.values()):
            # Form is valid, process the data
            form_data = {
                'name': name,
                'email': email,
                'age': int(age) if age else None,
                'bio': bio,
                'preferences': preferences,
                'account_type': account_type
            }
            flash('Form submitted successfully!', 'success')
        else:
            # Form has errors, show them to the user
            flash('Please correct the errors below.', 'error')

    return render_template('basic_form.html', form_data=form_data, errors=errors)


@app.route('/wtforms', methods=['GET', 'POST'])
def wtforms_demo():
    """Demonstrate WTForms integration."""
    form = RegistrationForm()
    form_data = session.pop('form_data', None)

    if form.validate_on_submit():
        # Form is valid, process the data
        form_data = {
            'username': form.username.data,
            'email': form.email.data,
            'age': form.age.data,
            'country': form.country.data,
            'biography': form.biography.data,
            'agree_terms': form.agree_terms.data,
            'subscribe_newsletter': form.subscribe_newsletter.data
        }
        session['form_data'] = form_data
        flash('Registration successful!', 'success')
        # In a real app, you would save this data to a database
        return redirect(url_for('wtforms_demo'))

    return render_template('wtforms.html', form=form, form_data=form_data)


@app.route('/upload', methods=['GET', 'POST'])
def file_upload():
    """Demonstrate file upload handling."""
    uploaded_files = []
    errors = {}

    if request.method == 'POST':
        # Get form data
        name = request.form.get('name', '').strip()

        # Validate name
        name_error = validate_name(name)
        if name_error:
            errors['name'] = name_error

        # Handle file uploads
        avatar = request.files.get('avatar')
        documents = request.files.getlist('documents')

        # Validate avatar
        avatar_types = {'png', 'jpg', 'jpeg', 'gif'}
        if avatar and avatar.filename:
            if not allowed_file(avatar.filename, {'png', 'jpg', 'jpeg', 'gif'}):
                errors['avatar'] = f'Only image files like {avatar_types} are allowed for avatar'
            elif avatar.content_length > 2 * 1024 * 1024:  # 2MB
                errors['avatar'] = 'Avatar file size must be less than 2MB'

        # Validate documents
        doc_types = {'pdf', 'doc', 'docx', 'txt', 'md'}
        for doc in documents:
            if doc and doc.filename:
                if not allowed_file(doc.filename, doc_types):
                    errors['documents'] = f'Only document files like {doc_types} are allowed'
                elif doc.content_length > 5 * 1024 * 1024:  # 5MB
                    errors['documents'] = 'Document file size must be less than 5MB'

        # Check if there are any errors
        if not any(errors.values()) and name:
            # Form is valid, process the files
            if avatar and avatar.filename:
                filename = secure_filename(f"avatar_{name}_{avatar.filename}")
                filepath = os.path.join(app.config['UPLOAD_FOLDER'], filename)
                avatar.save(filepath)
                uploaded_files.append({
                    'filename': filename,
                    'size': os.path.getsize(filepath)
                })

            for doc in documents:
                if doc and doc.filename:
                    filename = secure_filename(f"doc_{name}_{doc.filename}")
                    filepath = os.path.join(app.config['UPLOAD_FOLDER'], filename)
                    doc.save(filepath)
                    uploaded_files.append({
                        'filename': filename,
                        'size': os.path.getsize(filepath)
                    })

            flash('Files uploaded successfully!', 'success')
        elif not name:
            flash('Please enter your name.', 'error')
        else:
            flash('Please correct the errors below.', 'error')

    return render_template('file_upload.html', uploaded_files=uploaded_files, errors=errors)


def allowed_file(filename, allowed_extensions):
    """Check if file extension is allowed."""
    return '.' in filename and \
        filename.rsplit('.', 1)[1].lower() in allowed_extensions


# Error handlers
@app.errorhandler(413)
def too_large(e):
    """Handle file too large error."""
    flash('File is too large. Maximum size is 16MB.', 'error')
    return redirect(request.url), 413


if __name__ == '__main__':
    app.run(
        host=os.getenv('FLASK_HOST', '127.0.0.1'),
        port=int(os.getenv('FLASK_PORT', 5000)),
        debug=os.getenv('FLASK_DEBUG', 'True').lower() == 'true'
    )
